Conversation
monitor/src/scheduler.rs
Outdated
| "Thread {} (`{}`) finished but there are other threads with the same start time ({}) in the `next` queue, namely {:?}", | ||
| // ...any other threads from the same start cycle still in `next` | ||
| // are slower siblings that lost the race — move them to `failed` | ||
| let sibling_names: Vec<String> = self |
There was a problem hiding this comment.
With #174 we now want to allow for different matching traces. Instead of cutting them of, we report all of them, which is why you can see things like Trace 0 and Trace 1 in the output.
We probably need to think of a better way to integrate multiple traces in the output format so that it can remain compatible with the interpreter. The easiest would probably be to define a keyword that signals the start of a new trace, so in the interpreter, you could then reset everything and execute the new trace.
There was a problem hiding this comment.
Agreed, though I should point out the tests that led me to this change were failing to produce any trace in the monitor. I think this was just a small thing that wasn't removed in #174. I've removed my logic as well as the old monitor logic and tested; I checked that the monitor is able to produce multiple traces now for this test.
| stall(7, 0) // [time: 1012.5ns -> 1037.5ns] (thread 46) | ||
| reset() // [time: 1062.5ns -> 1087.5ns] (thread 48) | ||
| reset() // [time: 1087.5ns -> 1100ns] (thread 49) | ||
| Trace 2: |
There was a problem hiding this comment.
Here you can see how your change means that the monitor only outputs a single trace instead of three different ones. However, we do want the monitor to output 3 traces for this example.
There was a problem hiding this comment.
Getting rid of the change in the above comment got us back up to two traces. The problem with getting the third trace is that the stall protocol didn't end in step(), so traces with it were getting thrown out for well-formedness reasons. We need that check for interpreter-monitor parity, or we get rid of the check, update well-formedness checks, and modify the interpreter to handle these.
I'm noticing a few of the monitor protocols, particularly for stalls, dont end in fork(); step() and just end with assertions. I'm wondering if this is an intentional exception to the well-formedness checks? when I add a fork(); step(); back in, I'm losing one of the three traces from the output. The reason is that the stall is now treated as a 2-cycle protocol, so the '1 cycle stall->1 cycle stall' trace disappears and so we go from 3 possible traces to just 2...
if the intention is to relax the well-formedness checks to allow only assertions after the last step(), we can do that
There was a problem hiding this comment.
I told Nikil this was probably a mistake on my part when I hand-wrote the protocols since I didn't run them through the interpreter -- I will investigate this!
| @@ -0,0 +1,32 @@ | |||
|
|
|||
| === Round-trip results === | |||
There was a problem hiding this comment.
Should this be committed to the repo? Or do you want to add the filename to the .gitignore.
scripts/roundtrip.py
Outdated
| monitor_cmd, shell=True, cwd=base_dir, | ||
| capture_output=True, text=True, | ||
| ) | ||
| if result.returncode == 0: |
There was a problem hiding this comment.
Do you plan on comparing the content of the .tx file that the monitor produces to the original .tx file?
6538a59 to
d4c98e0
Compare
This isn't integrated with snapshot testing or CI yet. The obvious ways I could think of to integrate this with Turnt seemed clunky or required more complex changes, so for now I handrolled much of the logic to collect the tests and parse the arguments (which makes it kinda janky). I don't expect this stuff to go into main branch via this PR, but if thisis valuable we can figure out how to do that properly later.
After implementing this, I found a few potential "bugs" in the interpreter+monitor.
How the round-trip test works:
For each
.txfile where the interpreter succeeds:.protfile.tx. We could do this now, but there are slight differences in the formatting that maybe we want to consider changing first?The script is at
scripts/roundtrip.pyand the generated output is inscripts/rountrip.outCurrent output:
Monitor bugs found and fixed (kinda?)
1. FST files with no time entries crash
fst-reader(combinational-only designs)Affected tests:
add_combinational.tx,passthrough_combdep.txRoot cause: Designs like add_d0 are purely combinational. When the interpreter runs a single cycle on them, the generated FST has no time entries. The
fst-readercrate then panics attime_chain[0](index out of bounds on an empty vec).Quick Fix: Added an extra
sim_step()at the end ofexecute_todos()inprotocols/src/scheduler.rsso the FST always has at least one time entry. This empty cycle, from what I can tell, doesn't break anything for the monitor?2. Monitor panics when a protocol argument is never mapped to a pin
Affected tests:
add_combinational.tx(protocoladd_combinational_illegal_observation_in_conditionalhasin b: u32but doesDUT.b := X, sobis never mapped to a trace value).There are a few things going on with this test. One is that I believe that the
add_combinational_illegal_observation_in_conditionalis actually being picked up by the monitor as a valid trace, despite it being illegal from the perspective of the interpreter. If it was noted to be illegal, we wouldnt get the following downstream error:Root cause:
to_protocol_applicationinmonitor/src/interpreter.rsdidunwrap_or_else(|| panic!(...))when looking up an argument inargs_mapping.Quick Fix: missing args are serialized as
"?"instead of panicking. This could also be serialized as "X"? A protocol might have an argument it never uses, and that shouldn't be an error, I think. Let me know if I am wrong. This failure in general definitely requires greater investigation. Regardless of the true upstream fix for theadd_combinationaltest, I think it is reasonable for people to write valid protocols with an unused arg, and the monitor might deal with that more gracefully than it does now, unless unknown params cause other issues in monitor tractability..3. Monitor kills scheduler when a finished thread has slower siblings still running
Affected tests:
both_threads_pass.tx.Root cause:
validate_finished_and_failed_threadsinmonitor/src/scheduler.rsreturned an error if a thread finished but sibling threads from the same start cycle were still in thenextqueue. This is wrong when protocols have different lengths (e.g.,addfinishes in 2 cycles butwait_and_addtakes longer). Ernest's meta scheduling thing is able to handle keeping the other "slower" trace around, but the current monitor logic was erroring instead. So, I just deleted that block of code. Now, both traces become valid.Quick Fix: Instead of returning
SchedulerError::NoTransactionsMatch, move the slower sibling threads fromnexttofailed.4. Empty blocks in monitor cause premature exits
Affected tests:
passthrough_combdep.tx.Root cause: In
monitor/src/interpreter.rs,evaluate_stmtforStmt::Blockwith an empty body returnedOk(None)(signaling "thread is done"), but it should have returnedOk(self.next_stmt_map[stmt_id])to continue to the next statement in the parent scope. An emptyifbranch likeif (cond) { } else { }would cause the thread to terminate early.Actual Fix: Changed empty
Blockhandling to usenext_stmt_mapinstead of returningOk(None). This reflects the interpreter logic. I think this was a bug in the interpreter that was only recently discovered a few months ago and patched by me, which is probably why the monitor logic is off.5. Protocols that don't end with
step()should be discardedAffected tests:
both_threads_pass.tx(protocoladd_doesnt_end_in_stepends withfork()instead ofstep()).Root cause: When a thread finishes execution (
Ok(None)inrun_thread_till_next_step), the monitor unconditionally added it to thefinishedqueue. Ill-formed protocols that don't end withstep()would get treated as successful matches.Quick Fix: Added a check in
run_thread_till_next_step: if the last executed statement isn'tStmt::Step, the thread is moved tofailedinstead offinished.A weird side effect of this was that this rule also affects the
stallprotocol in the AXI stream tests. Thestallprotocol has assertions after itsstep():I am sure these tests were written as such for a reason, but I'm confused as to why they yield valid results if the Protocols are not well-formed.